﻿using System;
using System.Data.Linq;
using System.Diagnostics;
using System.Linq;
using Magiq.Providers.Sql.Query;
using Magiq.Tests.Sql.TestModel;
using NUnit.Framework;

namespace Magiq.Tests.Sql
{
    [TestFixture, Explicit]
    public class CompiledQueriesBenchmarkTest
    {
        const int Tests = 5000;

        [SetUp]
        public void SetUp()
        {
            MagiqToSqlTestConfigurator.Current.SetUp();
            domain = new CollectionTestDomain();
            MagiqToSqlTestConfigurator.Current.Persist(domain.ChildItems);
            MagiqToSqlTestConfigurator.Current.Persist(domain.Parents);
            domain.AddRelations();
            MagiqToSqlTestConfigurator.Current.Reload();
        }

        [TearDown]
        public void TearDown()
        {
            MagiqToSqlTestConfigurator.Current.TearDown();
        }

        private const string value = "lala";

        [Test]
        public void BenchmarkForEntitySets()
        {
            TestEntitySet("magiq - not compiled", (p, dc) => p.Query(x=>x.Items).Where(x => x.Name == value).OrderBy(x => x.Id));
            TestEntitySet("magiq - compiled", (p, dc) => p.Query(x => x.Items).CompiledQuery("test", (q, n) => q.Where(x => x.Name == n).OrderBy(x => x.Id), value));
            TestEntitySet("linq2sql - not compiled", (p, dc) => dc.GetTable<ChildItem>().Where(x => x.ParentId == p.Id && x.Name == value).OrderBy(x => x.Id));
            TestEntitySet("linq2sql - compiled", (p, dc) => CompiledLinqEntitySet(dc, p));
        }

        [Test]
        public void BenchmarkForTables()
        {
            TestTable("magiq - not compiled", (p, dc) => p.Where(x => x.Name == value).OrderBy(x => x.Id));
            TestTable("magiq - compiled", (p, dc) => p.CompiledQuery("test2", (q, n) => q.Where(x => x.Name == n).OrderBy(x => x.Id), value));
            TestTable("linq2sql - not compiled", (p, dc) => p.Where(x => x.Name == value).OrderBy(x => x.Id));
            TestTable("linq2sql - compiled", (p, dc) => CompiledLinqTable(dc));
        }


        private CollectionTestDomain domain;
        private Func<DataContext, int, string, IQueryable<ChildItem>> compiledEntitySet;
        private Func<DataContext, string, IQueryable<ChildItem>> compiledTable;
        
        private static void TestEntitySet(string title, Func<Parent, DataContext, IQueryable<ChildItem>> action)
        {
            
            MagiqToSqlTestConfigurator.Current.Reload();
            var p = MagiqToSqlTestConfigurator.Current.GetQueryable<Parent>().Single(x => x.Id == 1);
            var dc = MagiqToSqlTestConfigurator.Current.DataContext;
            action(p, dc).ToArray();

            var s = new Stopwatch();
            s.Start();
            for (var i = 0; i < Tests; i++)
            {
               // MagiqToSqlTestConfigurator.Current.Reload();
                p = MagiqToSqlTestConfigurator.Current.GetQueryable<Parent>().Single(x => x.Id == 1);
                dc = MagiqToSqlTestConfigurator.Current.DataContext;
                action(p, dc).ToArray();
            }
            s.Stop();
            Console.WriteLine("{0}: {1} millisecond", title, s.ElapsedMilliseconds);
        }

        private static void TestTable(string title, Func<IQueryable<ChildItem>, DataContext, IQueryable<ChildItem>> action)
        {
            MagiqToSqlTestConfigurator.Current.Reload();
            var p = MagiqToSqlTestConfigurator.Current.GetQueryable<ChildItem>();
            var dc = MagiqToSqlTestConfigurator.Current.DataContext;
            action(p, dc).ToArray();

            var s = new Stopwatch();
            s.Start();

            for (var i = 0; i < Tests; i++)
            {
               // MagiqToSqlTestConfigurator.Current.Reload();
                p = MagiqToSqlTestConfigurator.Current.GetQueryable<ChildItem>();
                dc = MagiqToSqlTestConfigurator.Current.DataContext;
                action(p, dc).ToArray();
            }
            s.Stop();
            Console.WriteLine("{0}: {1} millisecond", title, s.ElapsedMilliseconds);
        }

        private IQueryable<ChildItem> CompiledLinqEntitySet(DataContext dc, Parent parent)
        {
            if (compiledEntitySet == null)
            {
                compiledEntitySet = CompiledQuery.Compile<DataContext, int, string, IQueryable<ChildItem>>(
                        (d, id, n) => d.GetTable<ChildItem>()
                                   .Where(x => x.ParentId == id && x.Name == value)
                                   .OrderBy(x => x.Id));
            }

            return compiledEntitySet(dc, parent.Id, value);
        }

        private IQueryable<ChildItem> CompiledLinqTable(DataContext dc)
        {
            if (compiledTable == null)
            {
                compiledTable = CompiledQuery.Compile<DataContext, string, IQueryable<ChildItem>>(
                        (d, id) => d.GetTable<ChildItem>()
                                   .Where(x => x.Name == value)
                                   .OrderBy(x => x.Id));
            }

            return compiledTable(dc, value);
        }
    }
}